//
// Created by chizuru on 2025/9/25.
//

#include "SerialPortAdapter.h"
#include "libserialport.h"
#include "libserialerr.h"
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else define(__linux__)
#include <unistd.h>
#endif
SerialPortAdapter::SerialPortAdapter(std::string portname): portname(portname){}

int SerialPortAdapter::configure(std::any baud, std::any dataBits, std::any parity, std::any stopBits,
                                 std::any flowctrl) {
    int  _baud;
    int _dataBits;
    sp_parity _parity;
    int _stopBits;
    sp_flowcontrol _flowctrl;
    try {
        _baud = std::any_cast<int>(baud);
        _dataBits = std::any_cast<int>(dataBits);
        _parity = std::any_cast<sp_parity>(parity);
        _stopBits = std::any_cast<int>(stopBits);
        _flowctrl = std::any_cast<sp_flowcontrol>(flowctrl);
    }catch (...) { return -1; }

    if (sp_set_baudrate(port, _baud) != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage(SP_ERR_FAIL) << std::endl;
        return -1;
    }
    if (sp_set_bits(port, _dataBits) != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage(SP_ERR_FAIL) << std::endl;
        return -1;
    }
    if (sp_set_parity(port, _parity) != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage(SP_ERR_FAIL) << std::endl;
        return -1;
    }
    if (sp_set_stopbits(port, _stopBits) != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage(SP_ERR_FAIL) << std::endl;
        return -1;
    }
    if (sp_set_flowcontrol(port, _flowctrl) != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage(SP_ERR_FAIL) << std::endl;
        return -1;
    }
    return 0;
}

int SerialPortAdapter::write(const char *data, size_t size) {
    std::vector<char> buffer(data, data + size);
    int byteWritten = sp_blocking_write(port, buffer.data(), size, 100);
    if (byteWritten < 0) {
        std::cerr << printLibSerialPortErrorMessage((enum sp_return)byteWritten) << std::endl;
    }
    return byteWritten;
}

int SerialPortAdapter::read(char *buf, size_t maxSize) {
    int byteRead = sp_blocking_read(port, buf, maxSize - 1, 100);
    if (byteRead < 0) {
        std::cerr << printLibSerialPortErrorMessage((enum sp_return)byteRead) << std::endl;
    }
    return byteRead;
}

int SerialPortAdapter::flushReadAndWrite() {
    auto ret = sp_flush(port, SP_BUF_BOTH);
    if (ret != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage((enum sp_return)ret) << std::endl;
        return -1;
    }
    return ret;
}

OperationResult SerialPortAdapter::connect() {
    if (sp_get_port_by_name(portname.c_str(), &port) != SP_OK || port == nullptr) {
        return OperationResult::Fail(ErrorCode::ConnectionFailure, "PORT NOT FOUND");
    }

    if (sp_open(port, SP_MODE_READ_WRITE) != SP_OK) {
        sp_free_port(port);
        return OperationResult::Fail(ErrorCode::ConnectionFailure, printLibSerialPortErrorMessage(SP_ERR_FAIL));;
    }
    return OperationResult::Ok();
}

OperationResult SerialPortAdapter::close() {
    auto ret = sp_close(port);
    if (ret != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage((enum sp_return)ret) << std::endl;
        return OperationResult::Fail(ErrorCode::CloseFailure, printLibSerialPortErrorMessage((enum sp_return)ret));
    }
    return OperationResult::Ok();
}

bool SerialPortAdapter::isConnected() const {
    if (!port) return false;
    void *handle;
    auto sp_ret = sp_get_port_handle(port, &handle);
    if (sp_ret != SP_OK) {
        std::cerr << printLibSerialPortErrorMessage((enum sp_return)sp_ret) << std::endl;
        return false;
    }
    if (handle == NULL) return false;
#ifdef _WIN32
    // 尝试 ClearCommError
    COMSTAT comstat;
    DWORD errors = 0;
    BOOL ok = ClearCommError(handle, &errors, &comstat);
    if (!ok) {
        DWORD err = GetLastError();
        // 常见错误表明设备已移除/不可用，例如 ERROR_DEVICE_NOT_CONNECTED / ERROR_GEN_FAILURE / ERROR_INVALID_HANDLE
        if (err == ERROR_INVALID_HANDLE || err == ERROR_GEN_FAILURE || err == ERROR_DEVICE_NOT_CONNECTED)
            return false;
        // 其他错误也可以认为不可用或需要重试
        return false;
    }
#else define(__linux__)
    int status;
#if defined(TIOCMGET)
    if (ioctl(fd, TIOCMGET, &status) == -1) {
        // ioctl 失败，若 errno==ENODEV/EIO，通常表示设备被移除
        if (errno == ENODEV || errno == EIO) return false;
        // 其他 errno 不一定表示移除：可视情况处理
    }
#endif
#endif
    return true;
}
